/*
 * Copyright 2019 Zhang XueHai
 * you may not use this file except in compliance with the License.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.zhangxuehai.android.pdfviewer;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.SparseArray;
import android.view.MotionEvent;

import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import com.zhangxuehai.android.pdfviewer.annotation.BaseAnnotation;
import com.zhangxuehai.android.pdfviewer.annotation.MarkAnnotation;
import com.zhangxuehai.android.pdfviewer.annotation.PenAnnotation;
import com.zhangxuehai.android.pdfviewer.annotation.eraser.Eraser;
import com.zhangxuehai.android.pdfviewer.annotation.pen.Pen;
import com.shockwave.pdfium.util.Size;
import com.shockwave.pdfium.util.SizeF;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 注释管理器</br>
 * 负责生成&缓存注释
 * @author 章学海
 * @date 2019/12/26
 */
final class AnnotationManager{
    private PDFView pdfView;
    /**注释缓存*/
    SparseArray<List<BaseAnnotation>> annotations = new SparseArray<>();
    /**绘制中的注释*/
    BaseAnnotation drawingAnnotation;
    /**当前绘制的画笔*/
    private Pen pen;
    /**当前橡皮擦*/
    Eraser eraser;
    /**橡皮擦按下时所在的页码，-1未按下*/
    int eraserPage = -1;

    AnnotionListener annotionListener;

    AnnotationManager(PDFView pdfView) {
        this.pdfView = pdfView;
    }

    public void setAnnotionListener(AnnotionListener annotionListener) {
        this.annotionListener = annotionListener;
    }

    /**设置注释使用的画笔*/
    void setPen(Pen pen) {
        this.pen = pen;
    }
    /**设置橡皮擦*/
    void setEraser(Eraser eraser) {
        this.eraser = eraser;
    }
    /**触摸事件，生成注释*/
    boolean onTouch(MotionEvent event) {
        if(pdfView.getFunction() == PDFView.Function.PEN){
            return onPenTouch(event);
        } else if(pdfView.getFunction() == PDFView.Function.ERASER){
            return onEraserTouch(event);
        } else if(pdfView.getFunction() == PDFView.Function.MARK){
            return onMarkTouch(event);
        }
        return false;
    }
    /**生成画笔注释*/
    private boolean onPenTouch(MotionEvent event){
        //不处理第一根手指以外的事件
        if(event.getPointerId(event.getActionIndex()) !=0){
            return false;
        }
        int action = event.getActionMasked();
        int[] coord = CoordinateUtils.getPdfCoordinate(pdfView,event.getX(),event.getY());
        Size size = CoordinateUtils.getPdfPageSize(pdfView,coord[0]);
        Rect rect = new Rect(0,0,size.getWidth(),size.getHeight());
        boolean inPage = rect.contains(coord[1],coord[2]);
        //按下
        if(action == MotionEvent.ACTION_DOWN){
            //如果按下点的位置不在pdf范围内.跳出
            if(!inPage){
                return false;
            }
            //如果画笔不对，不处理
            if(pen instanceof Pen.WritePen){
                //noinspection unchecked
                this.drawingAnnotation = new PenAnnotation(coord[0],size, (Pen.WritePen) pen);
            }else{
                return false;
            }

            ((PenAnnotation) drawingAnnotation).data.add(CoordinateUtils.toPdfPointCoordinate(pdfView,coord[0],coord[1],coord[2]));
        }else if(action == MotionEvent.ACTION_MOVE){
            //移动
            if(drawingAnnotation !=null){
                //如果点在当前页码上添加数据点，否则结束注解
                if(drawingAnnotation.page == coord[0] && inPage){
                    ((PenAnnotation) drawingAnnotation).data.add(CoordinateUtils.toPdfPointCoordinate(pdfView,coord[0],coord[1],coord[2]));
                    pdfView.invalidate();
                }else{
                    if(((PenAnnotation) drawingAnnotation).data.size()>1){
                        addToAnnotions(drawingAnnotation);
                    }
                    drawingAnnotation = null;
                }
            }
        }else if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP){
            //抬起
            if(drawingAnnotation !=null){
                if(drawingAnnotation.page == coord[0] && inPage){
                    ((PenAnnotation) drawingAnnotation).data.add(CoordinateUtils.toPdfPointCoordinate(pdfView,coord[0],coord[1],coord[2]));
                }
                if(((PenAnnotation) drawingAnnotation).data.size()>1){
                    addToAnnotions(drawingAnnotation);
                }
                drawingAnnotation = null;
                pdfView.invalidate();
            }
        }
        return true;
    }
    /**生成橡皮擦*/
    private boolean onEraserTouch(MotionEvent event){
        //不处理第一根手指以外的事件
        if(event.getPointerId(event.getActionIndex()) !=0 || eraser==null){
            return false;
        }
        int action = event.getActionMasked();
        int[] coord = CoordinateUtils.getPdfCoordinate(pdfView,event.getX(),event.getY());
        Size size = CoordinateUtils.getPdfPageSize(pdfView,coord[0]);
        SizeF displaySize = pdfView.getPageSize(coord[0]);
        float scale = size.getWidth()/displaySize.getWidth();
        Rect rect = new Rect(0,0,size.getWidth(),size.getHeight());
        boolean inPage = rect.contains(coord[1],coord[2]);
        if(action == MotionEvent.ACTION_DOWN){
            //如果按下点的位置不在pdf范围内.跳出
            if(!inPage){
                return false;
            }
            eraserPage = coord[0];
            eraser.move(coord[1],coord[2]);
            removeAnnotion(eraser.erase(annotations.get(eraserPage),scale,pdfView.getZoom(),pdfView));
            pdfView.invalidate();
        }else if(action == MotionEvent.ACTION_MOVE){
            if(eraserPage == coord[0]){
                eraser.move(coord[1],coord[2]);
                removeAnnotion(eraser.erase(annotations.get(eraserPage),scale,pdfView.getZoom(),pdfView));
                pdfView.invalidate();
            }else{
                eraserPage = -1;
            }
        }else if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP){
            eraserPage = -1;
            pdfView.invalidate();
        }
        return true;
    }
    /**生成标记*/
    private boolean onMarkTouch(MotionEvent event){
        //不处理第一根手指以外的事件
        if(event.getPointerId(event.getActionIndex()) !=0){
            return false;
        }
        int action = event.getActionMasked();
        if(action!=MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE &&
                action != MotionEvent.ACTION_CANCEL && action != MotionEvent.ACTION_UP
                && action != MotionEvent.ACTION_POINTER_UP
        ){
            return false;
        }
        int[] coord = CoordinateUtils.getPdfCoordinate(pdfView,event.getX(),event.getY());
        Size size = CoordinateUtils.getPdfPageSize(pdfView,coord[0]);
        Rect rect = new Rect(0,0,size.getWidth(),size.getHeight());
        boolean inPage = rect.contains(coord[1],coord[2]);
        if(action == MotionEvent.ACTION_DOWN && !inPage){
            return false;
        }
        //noinspection ConstantConditions
        long pagePtr = pdfView.pdfFile.pdfDocument.getTextPagesPtr(coord[0]);
        //文本检索范围
        int searchRange =
                Math.min(
                    pdfView.pdfiumCore.getPageWidthPoint(pdfView.pdfFile.pdfDocument,coord[0]),
                    pdfView.pdfiumCore.getPageHeightPoint(pdfView.pdfFile.pdfDocument,coord[0])
                )/10;
        //转换为pdf页坐标
        SizeF pdfCoordinate = CoordinateUtils.toPdfPointCoordinate(pdfView,coord[0],coord[1],coord[2]);
        //检索的文本下标
        int textIndex = pdfView.pdfiumCore.getCharIndexAtPos(pagePtr,pdfCoordinate.getWidth(),pdfCoordinate.getHeight(),searchRange,searchRange);
        if(action == MotionEvent.ACTION_DOWN){
            if(textIndex == -1){
                return false;
            }
            if(pen instanceof Pen.MarkPen){
                //noinspection unchecked
                this.drawingAnnotation = new MarkAnnotation(coord[0],size, (Pen.MarkPen) pen,textIndex,pdfView.pdfiumCore.getTextRect(pagePtr,textIndex));
            }else{
                return false;
            }
        }else if(action == MotionEvent.ACTION_MOVE){
            if(drawingAnnotation !=null){
                if(drawingAnnotation.page == coord[0] && inPage){
                    if(textIndex == -1){
                        return true;
                    }else{
                        int start =((MarkAnnotation) drawingAnnotation).startIndex;
                        int count = start - textIndex;
                        //是否反序
                        boolean desc = count >0;
                        count = Math.abs(count);
                        ArrayList<RectF> rects = new ArrayList<>();
                        for (int i = 0; i <= count; i++) {
                            rects.add(pdfView.pdfiumCore.getTextRect(pagePtr,start + (desc?-1*i:i)));
                        }
                        //如果倒序选，反转排序。保证列表为正序选择
                        if(desc){
                            Collections.reverse(rects);
                        }
                        ((MarkAnnotation) drawingAnnotation).upada(textIndex,rects);
                    }
                }else{
                    if(((MarkAnnotation) drawingAnnotation).data.size()>=1){
                        drawingAnnotation.drawed = false;
                        addToAnnotions(drawingAnnotation);
                    }
                    drawingAnnotation = null;
                }
                pdfView.invalidate();
            }
        }else{
            if(drawingAnnotation !=null){
                if(drawingAnnotation.page == coord[0] && inPage && textIndex != -1){
                    int start =((MarkAnnotation) drawingAnnotation).startIndex;
                    int count = ((MarkAnnotation) drawingAnnotation).startIndex - textIndex;
                    //是否反序
                    boolean desc = count >0;
                    count = Math.abs(count);
                    ArrayList<RectF> rectFS = new ArrayList<>();
                    for (int i = 0; i <= count; i++) {
                        rectFS.add(pdfView.pdfiumCore.getTextRect(pagePtr,start + (desc?-1*i:i)));
                    }
                    //如果倒序选，反转排序。保证列表为正序选择
                    if(desc){
                        Collections.reverse(rectFS);
                    }
                    ((MarkAnnotation) drawingAnnotation).upada(textIndex,rectFS);
                }
                if(((MarkAnnotation) drawingAnnotation).data.size()>=1){
                    drawingAnnotation.drawed = false;
                    addToAnnotions(drawingAnnotation);
                }
                drawingAnnotation = null;
                pdfView.invalidate();
            }
        }
        return true;
    }
    /**从缓存删除*/
    private void removeAnnotion(@Nullable BaseAnnotation annotation){
        if(annotation != null){
            //在注解记录中删除
            annotations.get(annotation.page).remove(annotation);
            //清空绘画的注释
            Bitmap bm = pdfView.annotationDrawManager.cache.get(annotation.page);
            Canvas canvas = new Canvas(bm);
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            //重新标记注释为未绘制
            for (BaseAnnotation annotation1 : annotations.get(annotation.page)) {
                annotation1.drawed = false;
            }
            if(annotionListener!=null){
                annotionListener.onAnnotionRemove(annotation);
            }
        }
    }
    /**添加到缓存*/
    private void addToAnnotions(BaseAnnotation annotation){
        List<BaseAnnotation> list = annotations.get(annotation.page);
        if(list == null){
            list = new ArrayList<>();
        }
        list.add(annotation);
        annotations.put(annotation.page,list);
        if(annotionListener!=null){
            annotionListener.onAnnotionAdd(annotation);
        }
    }

    public void addAnnotions(List<BaseAnnotation> annotation){
        for (BaseAnnotation baseAnnotation : annotation) {
            List<BaseAnnotation> list = annotations.get(baseAnnotation.page);
            if(list == null){
                list = new ArrayList<>();
            }
            if(!list.contains(baseAnnotation)){
                list.add(baseAnnotation);
            }
            annotations.put(baseAnnotation.page,list);
        }
        pdfView.invalidate();
    }
    public List<BaseAnnotation> getAllAnnotation(){
        List<BaseAnnotation> all = new ArrayList<>();
        for (int i = 0; i < annotations.size(); i++) {
            List<BaseAnnotation> list = annotations.get(annotations.keyAt(i));
            all.addAll(list);
        }
        return all;
    }

    /**从缓存删除一页的注释*/
    synchronized void removeAnnotion(@IntRange(from = 0) int page){
        List<BaseAnnotation> list = annotations.get(page);
        if(list==null || list.size()==0){
            return;
        }
        list.clear();
        Bitmap bm = pdfView.annotationDrawManager.cache.get(page);
        if(bm == null || bm.isRecycled()){
            return;
        }
        Canvas canvas = new Canvas(bm);
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        pdfView.invalidate();
        if(annotionListener!=null){
            annotionListener.onAnnotionPageRemove(page);
        }
    }
    /**从缓存删除所以注释*/
    synchronized void removeAnnotionAll(){
        annotations.clear();
        int size = pdfView.annotationDrawManager.cache.size();
        for (int i = 0; i < size; i++) {
            int key=pdfView.annotationDrawManager.cache.keyAt(i);
            Bitmap bm = pdfView.annotationDrawManager.cache.get(key);
            if(bm!=null){
                bm.recycle();
                bm = null;
            }
        }
        pdfView.invalidate();
        if(annotionListener!=null){
            annotionListener.onAnnotionAllRemove();
        }
    }


}
